#include "p33Exxxx.h"

// CONFIGURE CLOCK  //////////////////////////////////////////////////////////////////////
_FOSCSEL(FNOSC_FRC);            
_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF  & POSCMD_XT);		// Clock Switching is enabled and Fail Safe Clock Monitor is disabled
                                                     	// OSC2 Pin Function: OSC2 is Clock Output
                                                       	// Primary Oscillator Mode: XT Crystal
_FWDT(FWDTEN_OFF);                                    	// Watchdog Timer Enabled/disabled by user software
WDTSWDisable;
// VARIABLE DEFINITIONS /////////////////////////////////////////////////////////////////       
//Commands issued by the master are 16 bit integers
enum SPI_COMMAND_e
{
    MASTER_NOP=1,                                 	// No operation
    READ_HEADER,                                   	// Request output buffer header from slave
    READ_WRD,                                     	// Waiting for mult     
    START,                                        	// start timer and analog sampling on the slave
    PAUSE,
    RESET                                         	// stop timer and analog sampling on the slave.  empty output buffer
};
//Status updates produced by slaves are 16 bit integers
typedef enum SPI_STATUS_e
{
    SLAVE_NOP=1,                                   	// No status change
    SDRTS,                                        	// Data Ready To Send from slave
    SLAVE_START,                                   	// Slave recieved and procesed start command from master, 
    SLAVE_RESET,                                 	// Slave received and processed reset from master
    START_FRAME,                                   	// a time tick has occurred
    BUFFER_EMPTY,                                  	// the master has pulled all data out of the slave buffer, it's now empty
    SLAVE_ERROR                                   	// if status word = error then second word contains the error code
}SPI_STATUS;            
enum ERROR_e
{
	BUFFER_OVERRUN,                               	// tail index of output buffer has overlapped head index        
	INVALID_BUFFER_INDEX                           	// The current buffer index has moved pas the buffer tail
} ERROR;        
// FUNCTIONS ////////////////////////////////////////////////////////////////////////////
void Delayus(unsigned int delay)
{
	int i;
	for (i = 0; i < delay; i++)
	{
	    __asm__ volatile ("repeat #39");
	    __asm__ volatile ("nop");
	}
}
unsigned int CRC_calculate(unsigned int *temp,unsigned int Len)
{
	 unsigned int j;
	 CRCWDATL=0x0000;                              	 // clear previous results
	 for(j=0;j<Len;j++)
	 {
	  CRCDATL =*temp++;                     	// write data into FIFO
	  if(CRCCON1bits.CRCFUL==1)            	// check if FIFO is full
	  {
	   Nop();
	   CRCCON1bits.CRCGO=1;          	// start shift register if FIFO is full
	  }       
	  while(CRCCON1bits.CRCFUL==1);           // wait until the CRC FIFO is not full any more to write next data      
	  Nop();
	  CRCCON1bits.CRCGO=0;
	 }
	 CRCCON1bits.CRCGO=1;
	 CRCDATL = 0x0000;                             	//appending PLEN+1 zeros (multiply by 2^16)
	 while(CRCCON1bits.CRCMPT!=1);                   //check if FIFO is empty
	 CRCCON1bits.CRCGO=0;                         	//stop CRC engine
	 return CRCWDATL;
}

// main////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
//// CONFIGURE I/O PINS  /////////////////////////////////////////////////////////////////
	__builtin_write_OSCCONL(OSCCON & ~(1<<6)); 	//unlock registers
	//Configure input pins
	RPINR20bits.SCK1R=0x7C;								//assign SCK1 to pin RP124/RG12
	RPINR20bits.SDI1R=0x7D;								//assign SDI1 to pin RP125/RG13
	RPINR21bits.SS1R=0x7F;								//assign SS1 tp pin RP127/RG15 	
	//Configure output pins
	RPOR15bits.RP126R=5;								//assign SDO1 to pin RP126/RG14
	//RPOR12bits.RP112R=5;								//assign SDO1 to pin RP112/RG0
	__builtin_write_OSCCONL(OSCCON | (1<<6));			//lock registers
// CONFIGURE OSCILLATOR TO RUN AT 120Mhz////////////////////////////////////////////////
	// Fosc= Fin*M/(N1*N2), Fcy=Fosc/2
	// Fosc= 8M*60/(2*2)=120Mhz for 8M input clock
	PLLFBD=58;											// M=60
	CLKDIVbits.PLLPOST=0;								// N1=2
	CLKDIVbits.PLLPRE=0;								// N2=2
	OSCTUN=0;											// Tune FRC oscillator, if FRC is used	
	// Disable Watch Dog Timer
	RCONbits.SWDTEN=0;
	// Clock switching to incorporate PLL
	__builtin_write_OSCCONH(0x03);						// Initiate Clock Switch to Primary
														// Oscillator with PLL (NOSC=0b011)
	__builtin_write_OSCCONL(0x01);						// Start clock switching
	while (OSCCONbits.COSC != 0b011);					// Wait for Clock switch to occur
	while(OSCCONbits.LOCK!=1) {};						// Wait for PLL to lock		
// INITIALIZE SPI //////////////////////////////////////////////////////////////////////
	IFS0bits.SPI1IF = 0;  								// Clear the Interrupt flag
	IEC0bits.SPI1IE = 0; 								// Disable the interrupt
	// SPI1CON1 Register Settings
	SPI1CON1bits.DISSDO = 0; 							// SDOx pin is used by the module
	SPI1CON1bits.MODE16 = 1; 							// Communication is word-wide (16 bits)
	SPI1CON1bits.SMP = 0; 								// Cleared for slave mode
	// SPI Mode 1
	SPI1CON1bits.CKE = 0; 								// (CPHA=1)Serial output data changes on transition 
														// from Idle clock state to active clock state
	SPI1CON1bits.CKP = 0; 								// (CPOL=0) Idle state for clock is a low level; active 
														// state is a high level
	SPI1CON1bits.SSEN=1;								// Slave Select pin is used by module
	SPI1STATbits.SPIROV = 0;  							// No Receive Overflow has occurred
	SPI1CON1bits.MSTEN = 0; 							// Master mode disabled
	SPI1STATbits.SPIEN = 1; 							// Enable SPI module
	IFS0bits.SPI1IF = 0;	
// CONFIGURE TIMER 1 TO TICK EVERY ~1ms (may have to lengthen this time)  ///////////////
    T1CONbits.TON = 0;                               	// Disable Timer
    T1CONbits.TCS = 0;                               	// Select internal instruction cycle clock 
    T1CONbits.TGATE = 0;                           		// Disable Gated Timer mode
    T1CONbits.TCKPS = 0b10;                          	// Select 1:64 Prescaler
    TMR1 = 0x0000;                                  	// Clear timer register
    PR1 = 938;//425;//1875;                   			// fire interrupt every ms 1/Fcy * 64 * 938=.001 seconds Fcy=Fosc/2=60MHZ
    IPC0bits.T1IP = 1;                                	// Set Timer 1 Interrupt Priority Level
    IFS0bits.T1IF = 0;                               	// Clear Timer 1 Interrupt Flag
    IEC0bits.T1IE = 0;//1;                            	// Enable Timer1 interrupt
    T1CONbits.TON =0;                               	// Stop Timer.  Timer ( and operation) won't start until START command is recieved
//// CONFIGURE CRC MODULE  /////////////////////////////////////////////////////////////////
//      CRCCON1bits.CRCEN=1;                            // enable CRC
//      CRCCON2bits.PLEN=0x0F;                          // configure the polynomial width (16 bit)=Length of poly-1
//      CRCXORL= 0x1021;                                // configure polynomial data
//      CRCXORH=0x0000;
//      CRCCON2bits.DWIDTH=0x0F;                        // configure the data width (16 bits)
//      CRCCON1bits.LENDIAN=0;                          // set the data shift direction
//      CRCCON1bits.CRCISEL=0;                          // select the interrupt source
//      IFS4bits.CRCIF=0;                               // clear the interrupt bit
//      IEC4bits.CRCIE=0;                               // disable the interrupt
// CONFIGURE ADC ///////////////////////////////////////////////////////////////////////
      ANSELB=0x0000;                                    //port B pins configured as digital I/O
      ANSELBbits.ANSB4=1;                               // set AN4 as analog input ( temp sensor)
      ANSELBbits.ANSB5=1;                               // set AN5 as analog input( potentiometer)
      AD1CON1bits.ASAM=1; 								//auto sample
      AD1CON1bits.SSRCG=0; 								// Allow SSRC setting = 111
      AD1CON1bits.SSRC=111; 							//auto convert
      AD1CON1bits.AD12B=0; 								//10 bit operation ( to llow scanning on CH0)
      AD1CON2bits.SMPI=1;  								// No DMA, so causes interrupt every SMPI+1 sample/conversion
      AD1CON2bits.CHPS=00; 								//convert channel 0
      AD1CON2bits.CSCNA=1; 								// scan inputs for CH0
      AD1CON3bits.ADCS=5; 								//number of Tcy(instruction clock cycles=(5*16.67 = 86ns)) per Tad (ADC clock cycle.(76ns min)) Assumes 60 MIPS
      AD1CON3bits.SAMC=20; 								//number of Tad to auto sample ( 2 min, as per datasheet)
      AD1CON3bits.ADRC=0;  								//clock derived from system clock
      AD1CON4bits.ADDMAEN=0;  							//disable DMA, use 16 word buffer
      AD1CSSL=0x0030; 									//scan AN4 & AN5
      AD1CHS0bits.CH0SA = 0; 							// CH0SA bits ignored for CH0 +ve
      AD1CHS0bits.CH0NA = 0;							// Select VREF- for CH0 -ve input
      AD1CON1bits.ADON=1; 								// allow 20 us to settle
      Delayus(20);
// DEFINE VARIABLES ///////////////////////////////////////////////////////////////////
	SPI_STATUS SpiStatus;
	int 
		DataIn=0,
		temp;
	unsigned int 
		max_seq=10, 									// Number of time frames in output buffers
		num_obufs=6,									// Number of output buffers
		SpiCommand,										// command sequence recived from master
		DataOut[max_seq*num_obufs],
		word_count=0,									// number of words that have been transmitted to master during a data frame
		data_out_idx=0,
		max_idx,										// maximum number of words in a dataframe
		TimeFrameCounter=0;
	unsigned long long int 
		TIME_INDEX=0; 									// count how many timer ticks have occured for all time
// INITIALIZE VARIABLES //////////////////////////////////////////////////////////////
	memset(DataOut,0,(max_seq*num_obufs)*2);
	max_idx=num_obufs*max_seq,
	_AD1IF=0;  											// clear interrupt flag to start sampling
// MAIN LOOP /////////////////////////////////////////////////////////////////////////
	while( 1)
	{		
// SPI OPERATIONS ////////////////////////////////////////////////////////////////////		
		if(SPI1STATbits.SPITBF==0)
		{
			if (word_count==(max_idx))    	// At least one data frame has been sent, starting a new data frame
		    {
		    	word_count=0;
		    }
			SPI1BUF=DataOut[word_count];
		}			
		if(SPI1STATbits.SPIRBF==1)
		{       
			SPI1STATbits.SPIROV = 0;
			SpiCommand=SPI1BUF;     
			word_count=word_count+1;
			switch (SpiCommand)
			{
				case RESET:
					SPI1STAT = 0;    // clear SPI
			    	SPI1STATbits.SPIEN = 1;
					T1CONbits.TON =0; 
					TIME_INDEX=0;
					TMR1 = 0x0000;
					memset(DataOut,0,max_idx*2);
					word_count=0;
					data_out_idx=0;
					SpiStatus=SLAVE_RESET;
					break;
				case START:
					T1CONbits.TON =1;
					break;
				default:
				;
			}		
		}
// LOCAL OPERATIONS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Slave operation is divided into 2 phases.  1) Read/send data to master over SPI bus.  This is a time sensitive process and cannot be interrupted.
// 2) Perform local operations, in the code below.  This code will not execute until all words in a dataframe have been processed
// there are "max_idx" words in a dataframe (or: num_obufs*max_seq)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		if (word_count==1)								// Either we just got the START command or the last word in a data frame was just read by the master
                                      // Word 1 of frame 1 from master contains a START command
		{			
			if ((_T1IF)&&(_AD1IF))
			{
				if (TimeFrameCounter==25) 				// tick every 25 milliseconds
				{
				    TIME_INDEX=TIME_INDEX+1; 			// increment the timer tick count
				    _AD1IF=0;  //clear interrupt flag to start sampling
				    SpiStatus=START_FRAME;
				    TimeFrameCounter=0;
				}
				_T1IF=0;   //restart timer
				TimeFrameCounter=(TimeFrameCounter+1); 
			}  
			if (_AD1IF && (SpiStatus==START_FRAME))
			{
				SpiStatus=SDRTS;
				DataOut[data_out_idx++]=(TIME_INDEX >> 48) & 0xFFFF;	
				DataOut[data_out_idx++]=(TIME_INDEX >> 32) & 0xFFFF;
				DataOut[data_out_idx++]=(TIME_INDEX >> 16) & 0xFFFF;
				DataOut[data_out_idx++]= TIME_INDEX& 0xFFFF;
				DataOut[data_out_idx++]= ADC1BUF0;
				DataOut[data_out_idx++]= ADC1BUF1;
				data_out_idx=data_out_idx % max_idx;	// wrap index around circular buffer if it exceeds max_idx
			}	
		}// if (word_count==1) ///////////////////////////////////////////////////////////             
	} // main while loop  //////////////////////////////////////////////////////////////
} // main/////////////////////////////////////////////////////////////////////////////
